/* picoc main header file - this has all the main data structures and
 * function prototypes. If you're just calling picoc you should look at the
 * external interface instead, in picoc.h */
#include <csetjmp>
#include <corecrt_wstdio.h>
#ifndef INTERPRETER_H
#define INTERPRETER_H

//#include "platform.h"
#ifndef NULL
#define NULL 0
#endif
 /* picoc version number */
#ifdef VER
/* VER, the git hash number, and TAG are obtained via the Makefile */
#define PICOC_VERSION TAG " r" VER
#else
#define PICOC_VERSION "v2.3.2"
#endif

/* this has to be a macro, otherwise errors will occur due to
    the stack being corrupt */
#define PicocPlatformSetExitPoint(pc) setjmp((pc)->picocExitBuf)

/*
#ifndef min
#define min(x,y) (((x)<(y))?(x):(y))
#endif
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
*/
/* Get the name of a type */
// #define typename(x) _Generic((x),   \
//     _Bool: "_Bool", \
//     unsigned char: "unsigned char", \
//     char: "char", \
//     signed char: "signed char", \
//     short int: "short int", \
//     unsigned short int: "unsigned short int",   \
//     int: "int", \
//     unsigned int: "unsigned int", \
//     long int: "long int", \
//     unsigned long int: "unsigned long int", \
//     long long int: "long long int", \
//     unsigned long long int: "unsigned long long int", \
//     float: "float", \
//     double: "double", \
//     long double: "long double", \
//     char *: "pointer to char", \
//     void *: "pointer to void", \
//     int *: "pointer to int", \
//     default: "other") (x)
#if defined(__hppa__) || defined(__sparc__)
/* the default data type to use for alignment */
#define ALIGN_TYPE double
#else
/* the default data type to use for alignment */
#define ALIGN_TYPE void*
#endif

#define MEM_ALIGN(x) (((x) + sizeof(ALIGN_TYPE)-1) & ~(sizeof(ALIGN_TYPE)-1))

/* for debugging */
#define PRINT_SOURCE_POS() { \
                                PrintSourceTextErrorLine(parser->pc->cStdOut, \
                                                         parser->fileName, \
                                                         parser->sourceText, \
                                                         parser->line, \
                                                         parser->characterPos); \
                                PlatformPrintf(parser->pc->cStdOut, "\n"); \
                            }

#define PRINT_TYPE(typ) PlatformPrintf(parser->pc->cStdOut, "%t\n", typ);

typedef FILE IOFILE;

/* coercion of numeric types to other numeric types */
#define IS_FP(v) ((v)->typ->base == kTypeFP)
#define FP_VAL(v) ((v)->val->fP)

/* ap -> AllowPointerCoercion = true | false */
#define IS_POINTER_COERCIBLE(v, ap) ((ap) ? ((v)->typ->base == kTypePointer) : 0)
#define POINTER_COERCE(v) ((int)(v)->val->pointer)

#define IS_INTEGER_NUMERIC_TYPE(t) ((t)->base >= kTypeInt && (t)->base <= kTypeUnsignedLong)
#define IS_INTEGER_NUMERIC(v) IS_INTEGER_NUMERIC_TYPE((v)->typ)
#define IS_NUMERIC_COERCIBLE(v) (IS_INTEGER_NUMERIC(v) || IS_FP(v))
#define IS_NUMERIC_COERCIBLE_PLUS_POINTERS(v,ap) (IS_NUMERIC_COERCIBLE(v) || IS_POINTER_COERCIBLE(v,ap))
#define GLOBAL_TABLE_SIZE (97)                /* global variable table */
#define STRING_TABLE_SIZE (97)                /* shared string table size */
#define STRING_LITERAL_TABLE_SIZE (97)        /* string literal table size */
#define RESERVED_WORD_TABLE_SIZE (97)         /* reserved word table size */
#define PARAMETER_MAX (16)                    /* maximum number of parameters to a function */
#define LINEBUFFER_MAX (256)                  /* maximum number of characters on a line */
#define LOCAL_TABLE_SIZE (11)                 /* size of local variable table (can expand) */
#define STRUCT_TABLE_SIZE (11)                /* size of struct/union member table (can expand) */


struct Table;
class Interpreter;

/* lexical tokens */
enum LexToken {
    /* 0x00 */ kTokenNone,
    /* 0x01 */ kTokenComma,
    /* 0x02 */ kTokenAssign,
               kTokenAddAssign,
               kTokenSubtractAssign,
               kTokenMultiplyAssign,
               kTokenDivideAssign,
               kTokenModulusAssign,
    /* 0x08 */ kTokenShiftLeftAssign,
               kTokenShiftRightAssign,
               kTokenArithmeticAndAssign,
               kTokenArithmeticOrAssign,
               kTokenArithmeticExorAssign,
    /* 0x0d */ kTokenQuestionMark,
               kTokenColon,
    /* 0x0f */ kTokenLogicalOr,
    /* 0x10 */ kTokenLogicalAnd,
    /* 0x11 */ kTokenArithmeticOr,
    /* 0x12 */ kTokenArithmeticExor,
    /* 0x13 */ kTokenAmpersand,
    /* 0x14 */ kTokenEqual,
               kTokenNotEqual,
    /* 0x16 */ kTokenLessThan,
               kTokenGreaterThan,
               kTokenLessEqual,
               kTokenGreaterEqual,
    /* 0x1a */ kTokenShiftLeft,
               kTokenShiftRight,
    /* 0x1c */ kTokenPlus,
               kTokenMinus,
    /* 0x1e */ kTokenAsterisk,
               kTokenSlash,
               kTokenModulus,
    /* 0x21 */ kTokenIncrement,
               kTokenDecrement,
               kTokenUnaryNot,
               kTokenUnaryExor,
               kTokenSizeof,
               kTokenCast,
    /* 0x27 */ kTokenLeftSquareBracket,
               kTokenRightSquareBracket,
               kTokenDot,
               kTokenArrow,
    /* 0x2b */ kTokenOpenBracket,
               kTokenCloseBracket,
    /* 0x2d */ kTokenIdentifier,
               kTokenIntegerConstant,
               kTokenFPConstant,
               kTokenStringConstant,
               kTokenCharacterConstant,
    /* 0x32 */ kTokenSemicolon,
               kTokenEllipsis,
    /* 0x34 */ kTokenLeftBrace,
               kTokenRightBrace,
    /* 0x36 */ kTokenIntType,
               kTokenCharType,
               kTokenFloatType,
               kTokenDoubleType,
               kTokenVoidType,
               kTokenEnumType,
    /* 0x3c */ kTokenLongType,
               kTokenSignedType,
               kTokenShortType,
               kTokenStaticType,
               kTokenAutoType,
               kTokenRegisterType,
               kTokenExternType,
               kTokenStructType,
               kTokenUnionType,
               kTokenUnsignedType,
               kTokenTypedef,
    /* 0x46 */ kTokenContinue,
               kTokenDo,
               kTokenElse,
               kTokenFor,
               kTokenGoto,
               kTokenIf,
               kTokenWhile,
               kTokenBreak,
               kTokenSwitch,
               kTokenCase,
               kTokenDefault,
               kTokenReturn,
    /* 0x52 */ kTokenHashDefine,
               kTokenHashInclude,
               kTokenHashIf,
               kTokenHashIfdef,
               kTokenHashIfndef,
               kTokenHashElse,
               kTokenHashEndif,
    /* 0x59 */ kTokenNew,
               kTokenDelete,
    /* 0x5b */ kTokenOpenMacroBracket,
    /* 0x5c */ kTokenEOF,
               kTokenEndOfLine,
               kTokenEndOfFunction,
               kTokenBackSlash
};

/* used in dynamic memory allocation */
struct AllocNode {
    unsigned int size;
    AllocNode *nextFree;
};

/* whether we're running or skipping code */
enum RunMode {
    kRunModeRun,                 /* we're running code as we parse it */
    kRunModeSkip,                /* skipping code, not running */
    kRunModeReturn,              /* returning from a function */
    kRunModeCaseSearch,          /* searching for a case label */
    kRunModeBreak,               /* breaking out of a switch/while/do */
    kRunModeContinue,            /* as above but repeat the loop */
    kRunModeGoto                 /* searching for a goto label */
};

/* parser state - has all this detail so we can parse nested files */
struct ParseState {
    Interpreter *pc;                  /* the picoc instance this parser is a part of */
    const unsigned char *pos;   /* the character position in the source text */
    char *fileName;             /* what file we're executing (registered string) */
    short int line;             /* line number we're executing */
    short int characterPos;     /* character/column in the line we're executing */
    RunMode mode;          /* whether to skip or run code */
    int searchLabel;            /* what case label we're searching for */
    const char *searchGotoLabel;/* what goto label we're searching for */
    const char *sourceText;     /* the entire source text */
    short int hashIfLevel;      /* how many "if"s we're nested down */
    short int hashIfEvaluateToLevel;    /* if we're not evaluating an if branch,
                                          what the last evaluated level was */
    char debugMode;             /* debugging mode */
    int scopeID;   /* for keeping track of local variables (free them after t
                      hey go out of scope) */
};

/* values */
enum BaseType {
    kTypeVoid,                   /* no type */
    kTypeInt,                    /* integer */
    kTypeShort,                  /* short integer */
    kTypeChar,                   /* a single character (signed) */
    kTypeLong,                   /* long integer */
    kTypeUnsignedInt,            /* unsigned integer */
    kTypeUnsignedShort,          /* unsigned short integer */
    kTypeUnsignedChar,           /* unsigned 8-bit number */ /* must be before unsigned long */
    kTypeUnsignedLong,           /* unsigned long integer */
    kTypeFP,                     /* floating point */
    kTypeFunction,               /* a function */
    kTypeMacro,                  /* a macro */
    kTypePointer,                /* a pointer */
    kTypeArray,                  /* an array of a sub-type */
    kTypeStruct,                 /* aggregate type */
    kTypeUnion,                  /* merged type */
    kTypeEnum,                   /* enumerated integer type */
    kTypeGotoLabel,              /* a label we can "goto" */
    kTypeType                   /* a type for storing types */
};

/* data type */
struct ValueType {
    BaseType base;             /* what kind of type this is */
    int arraySize;                  /* the size of an array type */
    int sizeNeed;                     /* the storage required */
    int alignBytes;                 /* the alignment boundary of this type */
    const char *identifier;         /* the name of a struct or union */
    ValueType *fromType;     /* the type we're derived from (or NULL) */
    ValueType *derivedTypeList;  /* first in a list of types derived from this one */
    ValueType *next;         /* next item in the derived type list */
    Table *members;          /* members of a struct or union */
    int onHeap;                     /* true if allocated on the heap */
    int staticQualifier;            /* true if it's a static */
};
struct Value;
/* function definition */
struct FuncDef {
    ValueType *returnType;   /* the return Value type */
    int numParams;                  /* the number of parameters */
    int varArgs;                    /* has a variable number of arguments after
                                        the explicitly specified ones */
    ValueType **paramType;   /* array of parameter types */
    char **paramName;               /* array of parameter names */
    void (*intrinsic)(ParseState *, Value *, Value **, int);            /* intrinsic call address or NULL */
    ParseState body;         /* lexical tokens of the function body if
                                        not intrinsic */
};
/* macro definition */
struct MacroDef {
    int numParams;              /* the number of parameters */
    char **paramName;           /* array of parameter names */
    ParseState body;     /* lexical tokens of the function body
                                        if not intrinsic */
};

/* values */
union AnyValue {
    char character;
    short shortInteger;
    int integer;
    long longInteger;
    unsigned short unsignedShortInteger;
    unsigned int unsignedInteger;
    unsigned long unsignedLongInteger;
    unsigned char unsignedCharacter;
    char *identifier;
    char arrayMem[2];       /* placeholder for where the data starts,
                                doesn't point to it */
    ValueType *typ;
    FuncDef FuncDef;
    MacroDef MacroDef;
    double fP;
    void *pointer;      /* unsafe native pointers */
};

struct Value {
    ValueType *typ;      /* the type of this Value */
    AnyValue *val;        /* pointer to the AnyValue which holds the actual content */
    Value *lValueFrom;   /* if an LValue, this is a Value our LValue is contained within (or NULL) */
    char valOnHeap;             /* this Value is on the heap */
    char valOnStack;            /* the AnyValue is on the stack along with this Value */
    char anyValOnHeap;          /* the AnyValue is separately allocated from the Value on the heap */
    char isLValue;              /* is modifiable and is allocated somewhere we can usefully modify it */
    int scopeID;                /* to know when it goes out of scope */
    bool outOfScope;
};





/* hash table data structure */
struct TableEntry {
    TableEntry *next;        /* next item in this hash chain */
    const char *declFileName;       /* where the variable was declared */
    unsigned short declLine;
    unsigned short declColumn;

    union TableEntryPayload {
        struct ValueEntry {
            char *key;              /* points to the shared string table */
            Value *val;      /* the Value we're storing */
        } v;                        /* used for tables of values */

        char key[1];                /* dummy size - used for the shared string table */

        /* defines a breakpoint */
        struct BreakpointEntry {
            const char *fileName;
            short int line;
            short int characterPos;
        } b;

    } p;
};

struct Table {
    short size;
    short onHeap;
    TableEntry **hashTable;
};

/* stack frame for function calls */
struct stackFrame {
    ParseState returnParser;         /* how we got here */
    const char *funcName;                   /* the name of the function we're in */
    Value *returnValue;              /* copy the return Value here */
    Value **parameter;               /* array of parameter values */
    int numParams;                          /* the number of parameters */
    Table localTable;                /* the local variables and parameters */
    TableEntry *localHashTable[LOCAL_TABLE_SIZE];
    stackFrame *previousStackFrame;  /* the next lower stack frame */
};

/* lexer state */
enum LexMode {
    kLexModeNormal,
    kLexModeHashInclude,
    kLexModeHashDefine,
    kLexModeHashDefineSpace,
    kLexModeHashDefineSpaceIdent
};

struct LexState {
    const char *pos;
    const char *end;
    const char *fileName;
    int line;
    int characterPos;
    const char *sourceText;
    enum LexMode mode;
    int emitExtraNewlines;
};

/* library function definition */
struct LibraryFunction {
    void (*func)(ParseState *parser, Value *, Value **, int);
    const char *prototype;
};

/* output stream-type specific state information */
union OutputStreamInfo {
    struct StringOutputStream {
        ParseState *parser;
        char *writePos;
    } str;
};

/* stream-specific method for writing characters to the console */
typedef void CharWriter(unsigned char, union OutputStreamInfo *);

/* used when writing output to a string - eg. sprintf() */
struct OutputStream {
    CharWriter *putch;
    union OutputStreamInfo i;
};

/* possible results of parsing a statement */
enum ParseResult { kParseResultEOF, kParseResultError, kParseResultOk };

/* a chunk of heap-allocated tokens we'll cleanup when we're done */
struct CleanupTokenNode {
    void *tokens;
    const char *sourceText;
    struct CleanupTokenNode *next;
};

/* linked list of lexical tokens used in interactive mode */
struct TokenLine {
    struct TokenLine *next;
    unsigned char *tokens;
    int numBytes;
};


/* a list of libraries we can include */
struct IncludeLibrary {
    char *includeName;
    void (*setupFunction)(Interpreter *pc);
    const LibraryFunction *funcList;
    const char *setupCSource;
    IncludeLibrary *nextLib;
};

#define FREELIST_BUCKETS (8)        /* freelists for 4, 8, 12 ... 32 byte allocs */
#define SPLIT_MEM_THRESHOLD (16)    /* don't split memory which is close in size */
#define BREAKPOINT_TABLE_SIZE (21)


/* the entire state of the picoc system */
class Interpreter {
public:
    static Interpreter &GetInstance() {
        static Interpreter interpreter;
        return interpreter;
    }
    char *PlatformReadFile(const char *fileName);
    /* include.c */
    void PicocIncludeAllSystemHeaders();
    /* initialize everything */
    void Initialize(int stackSize);
    /* free memory */
    void PicocCleanup();
    void PicocCallMain(int argc, char **argv);
    /* read and scan a file for definitions */
    void PicocPlatformScanFile(const char *fileName);
    /* include.c */
    void IncludeInit();
    void IncludeCleanup();
    void IncludeRegister(const char *includeName,
                                void (*setupFunction)(Interpreter *pc), const LibraryFunction *funcList,
                                const char *setupCSource);
    void IncludeFile(char *filename);
#ifdef DEBUG_HEAP
    void ShowBigList(Interpreter *pc);
#endif
    /* stack grows up from the bottom and heap grows down from
    the top of heap space */
    /* initialize the stack and heap storage */
    void HeapInit(int stackOrHeapSize);

    void HeapCleanup();

    /* allocate some space on the stack, in the current stack frame
     * clears memory. can return NULL if out of stack space */
    static void *HeapAllocStack(int size);

    /* allocate some space on the stack, in the current stack frame */
    static void HeapUnpopStack(int size);

    /* free some space at the top of the stack */
    static int HeapPopStack(void *addr, int size);

    /* push a new stack frame on to the stack */
    static void HeapPushStackFrame();

    /* pop the current stack frame, freeing all memory in the
        frame. can return NULL */
    static int HeapPopStackFrame();

    /* allocate some dynamically allocated memory. memory is cleared.
        can return NULL if out of memory */
    static void *HeapAllocMem(int size);

    /* free some dynamically allocated memory */
    static void HeapFreeMem(void *mem);

    /* picoc hash table module. This hash table code is used for both symbol tables
    * and the shared string table. */


    /* global initialisation for libraries */
    void LibraryInit();

    /* add a library */
    void LibraryAdd(const LibraryFunction *funcList);

    /* print a type to a stream without using printf/sprintf */
    void PrintType(ValueType *typ, IOFILE *stream);
    /* parser global data */
    Table globalTable;
    CleanupTokenNode *cleanupTokenList;
    TableEntry *globalHashTable[GLOBAL_TABLE_SIZE];

    /* lexer global data */
    TokenLine *interactiveHead;
    TokenLine *interactiveTail;
    TokenLine *interactiveCurrentLine;
    int lexUseStatementPrompt;
    AnyValue lexAnyValue;
    Value lexValue;
    Table reservedWordTable;
    TableEntry *reservedWordHashTable[RESERVED_WORD_TABLE_SIZE];

    /* the table of string literal values */
    Table stringLiteralTable;
    TableEntry *stringLiteralHashTable[STRING_LITERAL_TABLE_SIZE];

    /* the stack */
    stackFrame *topStackFrame;

    /* the Value passed to exit() */
    int picocExitValue;

    /* a list of libraries we can include */
    IncludeLibrary *includeLibList;

    /* heap memory */
    unsigned char *heapMemory;  /* stack memory since our heap is malloc()ed */
    void *heapBottom;           /* the bottom of the (downward-growing) heap */
    void *stackFrame;           /* the current stack frame */
    void *heapStackTop;         /* the top of the stack */

    AllocNode *freeListBucket[FREELIST_BUCKETS]; /* we keep a pool of freelist buckets to reduce fragmentation */
    AllocNode *freeListBig;    /* free memory which doesn't fit in a bucket */

    /* types */
    ValueType uberType;
    ValueType intType;
    ValueType shortType;
    ValueType charType;
    ValueType longType;
    ValueType unsignedIntType;
    ValueType unsignedShortType;
    ValueType unsignedLongType;
    ValueType unsignedCharType;
    ValueType fPType;
    ValueType voidType;
    ValueType typeType;
    ValueType functionType;
    ValueType macroType;
    ValueType enumType;
    ValueType gotoLabelType;
    ValueType *charPtrType;
    ValueType *charPtrPtrType;
    ValueType *charArrayType;
    ValueType *voidPtrType;

    /* debugger */
    Table breakpointTable;
    TableEntry *breakpointHashTable[BREAKPOINT_TABLE_SIZE];
    int breakpointCount;
    int debugManualBreak;

    IOFILE *cStdOut;
    IOFILE cStdOutBase;

    /* the picoc version string */
    const char *versionString;

    /* exit longjump buffer */
#if defined(UNIX_HOST) || defined(WIN32)
    jmp_buf picocExitBuf;
#endif

    /* string table */
    Table stringTable;
    TableEntry *stringHashTable[STRING_TABLE_SIZE];
    char *strEmpty;
    /* C library */
    int bigEndian;
    int littleEndian;
    /* endian-ness checking */
    const static int __ENDIAN_CHECK__ = 1;
private:
    Interpreter();

};


#ifdef DEBUGGER
/* debug.c */
extern void DebugInit(Interpreter *pc);
extern void DebugCleanup(Interpreter *pc);
extern void DebugCheckStatement(ParseState *parser);
extern void DebugSetBreakpoint(ParseState *parser);
extern int DebugClearBreakpoint(ParseState *parser);
extern void DebugStep(void)
#endif

#endif /* INTERPRETER_H */
